<template>
  <div>
    <div class="filter-container">
      <template v-for="(col,colIndex) in searchableColumns">
        <search-column-render :key="colIndex" :column="col" @change="handleSearchFieldChange(col,$event)"
                              v-model="filterParams[col.searchProp || col.prop]"
        />
      </template>
      <el-button
        v-for="item in searchbarButtons"
        :key="item.name"
        :icon="item.icon"
        :type="item.buttonType"
        size="mini"
        v-perm="item.name"
        @click="handleClickToolButton(item)"
      >
        {{ item.label }}
      </el-button>
      <el-button
        icon="el-icon-refresh"
        size="mini"
        style="margin-right:10px"
        v-if="searchableColumns.length"
        @click="handleResetTable()"
      >重置
      </el-button
      >
    </div>

    <loading-dialog v-model="isExporting" loading-text="正在导出，请稍等"/>

    <slot name="toolBar"/>
    <el-table
      v-loading="listLoading"
      :data="tableData"
      :row-key="tableOption.rowKey"
      :tree-props="tableOption.treeProps"
      :default-expand-all="tableOption.defaultExpandAll"
      :height="tableOption.tableHeight"
      @select-all="handleSelectAll"
      ref="multipleTable"
      border
      fit
      @select="handleSelect"
      @selection-change="handleSelectionChange"
    >
      <el-table-column type="expand" v-if="tableOption.expand" fixed="left">
        <template slot-scope="scope">
          <component :is="tableOption.expand.render" v-on="expandEventHandler(tableOption.expand)" :row="scope.row"/>
        </template>
      </el-table-column>
      <el-table-column
        v-if="tableOption.showSelection"
        type="selection"
        width="55"
        align="center"
      ></el-table-column>
      <el-table-column
        v-for="(col,colIndex) in tableColumns"
        :key="colIndex"
        :label="col.label"
        :fixed="col.fixed || false"
        :width="col.width || '160px'"
        :min-width="col.minWidth || '160px'"
        :align="col.align || 'center'"
      >
        <template slot-scope="scope">
          <column-render :column="col" :data="scope.row" @play="handlePlay" @previewImage="handlePreviewImage"
                         @previewDetail="handlePreviewDetail"
          />
        </template>
      </el-table-column>
      <slot name="column"/>
    </el-table>

    <div class="pagination-container" v-if="tableOption.pagination">
      <el-pagination
        :current-page.sync="listParams.pageNo"
        :page-sizes="[10, 20, 30, 50]"
        :page-size="listParams.pageSize"
        :total="total"
        background
        layout="total, sizes, prev, pager, next, jumper"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </div>
    <el-dialog :visible.sync="videoDialogShowed" width="840px">
      <video v-if="videoURL" ref="myVideo" width="800" height="450" :src="videoURL" controls="controls"></video>
    </el-dialog>
    <el-dialog :visible.sync="imageDialogShowed" width="840px" title="图片预览" append-to-body>
      <el-image v-for="(imgSrc,idx) in imgList" :src="imgSrc" :preview-src-list="imgList" :key="idx" fit="cover"
                class="previewImg"
      />
    </el-dialog>
    <el-dialog :visible.sync="previewDetailDialogShowed" width="840px" title="详情" append-to-body>
      <div style="line-height: 2">
        {{ previewDetail }}
      </div>
    </el-dialog>
  </div>
</template>

<script>
import ColumnType from '@/framework/constant/ColumnType'
import Selector from '@/framework/components/Selector'
import ColumnRender from '@/framework/components/table/ColumnRender'
import LoadingDialog from '@/framework/components/dialog/LoadingDialog'
import ExcelUtil from '@/framework/infra/excel/ExcelUtil'
import ColumnUtil from '@/framework/components/table/ColumnUtil'
import { mapGetters } from 'vuex'
import RouterUtil from '@/framework/infra/router/RouterUtil'
import TableButtonType from '@/framework/constant/TableButtonType'
import NameUtil from '@/framework/infra/name/NameUtil'
import DomUtil from '@/framework/infra/util/DomUtil'
import WinUtil from '@/framework/infra/util/WinUtil'
import SearchColumnRender from '@/framework/components/table/SearchColumnRender'
import CURDTable from '@/framework/components/table/CURDTable'
import ValueUtils from '@/framework/infra/value/ValueUtils'
import ObjectUtil from '@/framework/infra/object/ObjectUtil'

export default {
  name: 'RTable',
  components: { CURDTable, SearchColumnRender, LoadingDialog, ColumnRender, Selector },
  computed: {
    ...mapGetters(['routes']),
    pageTitle() {
      return RouterUtil.getByName(this.routes, this.$route.name)?.meta.title
    },
    buttons() {
      return RouterUtil.getButtonsByName(this.routes, this.$route.name, this.tableOption.groupId) || []
    },
    searchbarButtons() {
      const results = this.buttons.filter(el => el.type === TableButtonType.SEARCHBAR)
      if (this.searchableColumns.length === 0 && results.length > 0) {
        const item = results.find(el => el.name.endsWith(':list'))
        if (item) {
          item.label = '刷新'
          item.icon = 'el-icon-refresh'
        }
      }
      return results
    },
    searchableColumns() {
      return this.tableOption.columns.filter(el => el.searchable)
    },
    tableColumns() {
      return this.tableOption.columns.filter(el => !el.hiddenInTable)
    },
    tableOption() {
      const result = Object.assign({}, {
        columns: [],
        requestApi: null,
        pagination: true,
        initListParams: {},
        rowKey: 'id',
        requestApiNames: {
          list: 'list'
        },
        groupId: 0,
        showSelection: false,
        treeProps: {},
        defaultExpandAll: false,
        tableHeight: window.innerHeight - 200
      }, this.option)
      if (!result.pagination) {
        result.tableHeight += 60
      }
      return result
    }
  },
  data() {
    return {
      videoDialogShowed: false,
      imageDialogShowed: false,
      imgList: [],
      videoURL: null,
      previewDetail: '',
      previewDetailDialogShowed: false,
      ColumnType,
      tableData: [],
      listLoading: false,
      total: 0,
      filterParams: {},
      isExporting: false,
      listParams: {
        pageNo: 1,
        pageSize: 10
      }
    }
  },
  watch: {
    videoDialogShowed() {
      if (!this.videoDialogShowed && this.$refs.myVideo) {
        this.$refs.myVideo.pause()
      }
    }
  },
  methods: {
    handlePreviewImage(imgList) {
      this.imgList = imgList
      this.imageDialogShowed = true
    },
    handlePreviewDetail(text) {
      this.previewDetail = text
      this.previewDetailDialogShowed = true
    },
    handlePlay(v) {
      this.videoURL = v
      this.videoDialogShowed = true
    },
    expandEventHandler(expand) {
      const result = {}
      Object.keys(expand).filter(el => el.startsWith('on')).forEach(key => {
        result[NameUtil.lowerCaseFirstLetter(key.substr(2))] = expand[key]
      })
      return result
    },
    handleClickToolButton(button, arg) {
      const funcType = NameUtil.camelCase('handle', button.funcType)
      DomUtil.invokeClosetDomWithFuncName(this, funcType, arg)
    },
    handleSizeChange(val) {
      this.listParams.pageSize = val
      this.handleList()
    },
    handleSearchFieldChange(col, value) {
      if (col.type === ColumnType.DATETIME) {
        this.filterParams[col.startProp || (col.prop + 'Start')] = value[0]
        this.filterParams[col.endProp || (col.prop + 'End')] = value[1]
      }
    },
    handleCurrentChange(val) {
      this.listParams.pageNo = val
      this.handleList()
    },
    handleSelectionChange(val) {
      console.log(val)
      this.multipleSelection = val
    },
    handleSelect(selection, current) {
      // 判断selection中是否存在current,若是存在那么就代表是被勾选上了,若是不存在代表是取消勾选了
      const isChecked = !!selection.find(item => item.id === current.id)
      // 如果当前项被取消勾选
      if (!isChecked) {
        // 那么其所有的后代也应该被取消勾选
        this.toggleCheckedChildrens(selection, current, false)
        // 那么若是所有的兄弟都被取消勾选的话,其祖先也应该被取消勾选
        this.uncheckedParents(selection, current)
      } else { // 如果当前项被勾选
        // 那么若同一组的元素都被勾选了,那么父元素将也被勾选,依次往上类推
        this.checkedParents(selection)
        // 那么其所有的后代都要被勾选
        this.toggleCheckedChildrens(selection, current, true)
      }
    },
    toggleCheckedChildrens(selection, item, isChecked) {
      var _toggleCheckedChildrens = (data) => {
        data.find(element => {
          this.$refs.multipleTable.toggleRowSelection(element, isChecked)
          if (isChecked && !selection.find(item => item.id === element.id)) {
            selection.push(element)
          } else if (!isChecked && selection.find(item => item.id === element.id)) {
            for (let i = selection.length - 1; i >= 0; i--) {
              if (selection[i].id === element.id) {
                selection.splice(i, 1)
                break
              }
            }
          }
          _toggleCheckedChildrens(element.children || [])
        })
      }
      _toggleCheckedChildrens(item.children || [])
    },
    checkedParents(selection) {
      var _checkedParents = (element) => {
        const children = element.children
        if (children && children.length) {
          const anyChildrenChecked = children.some(child => {
            return _checkedParents(child)
          })
          if (anyChildrenChecked) {
            this.$refs.multipleTable.toggleRowSelection(element, true)
            if (!selection.find(item => item.id === element.id)) {
              selection.push(element)
            }
          }
        }
        return selection.find(item => item.id === element.id)
      }
      this.tableData.forEach(element => {
        _checkedParents(element)
      })
    },
    uncheckedParents(selection, item) {
      var _uncheckedParents = (element) => {
        const children = element.children
        if (children && children.length) {
          const allChildrenUnChecked = children.every(child => {
            return _uncheckedParents(child)
          })
          if (allChildrenUnChecked) {
            this.$refs.multipleTable.toggleRowSelection(element, false)
            for (let i = selection.length - 1; i >= 0; i--) {
              if (selection[i].id === element.id) {
                selection.splice(i, 1)
                break
              }
            }
            return true
          } else {
            return false
          }
        }
        return selection.every(item => item.id !== element.id)
      }
      this.tableData.forEach(element => {
        _uncheckedParents(element)
      })
    },
    handleSelectAll(selection) {
      const isAllSelected = this.$refs.multipleTable.store.states.isAllSelected
      const _handleSelectAll = (data) => {
        data.forEach(item => {
          this.$refs.multipleTable.toggleRowSelection(item, isAllSelected)
          _handleSelectAll(item.children || [])
        })
      }
      _handleSelectAll(this.tableData)
    },
    getMultipleSelection() {
      return this.multipleSelection || []
    },
    handleResetTable() {
      this.filterParams = {}
      this.handleList()
    },
    async handleExport() {
      this.isExporting = true
      try {
        const exportedColumns = this.tableOption.columns.filter(el =>
          el.type !== ColumnType.RICH_EDITOR &&
          el.type !== ColumnType.PASSWORD &&
          el.type !== ColumnType.IMAGE &&
          el.exported !== false &&
          el.hiddenInTable !== true
        )
        if (!this.tableOption.exportOption?.remote) {
          await this.clientExport(exportedColumns)
        } else {
          await this.remoteExport(exportedColumns)
        }
      } finally {
        this.isExporting = false
      }
    },
    async remoteExport(exportedColumns) {
      // 后端导出
      const columns = []
      for (const col of exportedColumns) {
        const item = {
          label: col.label,
          prop: col.displayProp || col.prop
        }
        const options = col.displayProp ? null : await ColumnUtil.parseOptionToMap(col)
        if (options) {
          item.options = options
        }
        columns.push(item)
      }
      const exportOption = {
        title: this.pageTitle,
        columns
      }
      const url = await this.tableOption.requestApi.export({
        filter: this.getFinalListParams(),
        exportOption
      })
      WinUtil.openWin(url)
    },
    getFinalListParams() {
      return {
        ...this.listParams,
        ...ValueUtils.getValue(this.tableOption.initListParams),
        ...this.filterParams
      }
    },
    // 前端导出
    async clientExport(exportedColumns) {
      let data
      if (!this.tableOption.pagination) {
        data = await this.tableOption.requestApi.list({
          ...this.getFinalListParams(),
          pageNo: 1,
          pageSize: 10000
        })
      } else {
        const result = await this.tableOption.requestApi.list({
          ...this.getFinalListParams(),
          pageNo: 1,
          pageSize: 10000
        })
        if (result) {
          data = result.data || []
        }
      }

      const header = exportedColumns.map(el => el.label)
      const exportedData = []
      if (data) {
        for (const row of data) {
          const exportedItem = []
          for (const col of exportedColumns) {
            const value = ObjectUtil.getPropValue(row, col.displayProp || col.prop)
            exportedItem.push(await ColumnUtil.getText(col, value))
          }
          exportedData.push(exportedItem)
        }
      }
      ExcelUtil.exportTable({
        header,
        data: exportedData,
        filename: this.pageTitle
      })
    },
    async handleList() {
      this.listLoading = true
      try {
        const result = await this.tableOption.requestApi[this.tableOption.requestApiNames.list](this.getFinalListParams())
        if (this.tableOption.pagination) {
          const {
            data,
            total
          } = result
          this.tableData = data || []
          this.total = total
        } else {
          this.tableData = result || []
        }
        this.$nextTick(() => {
          // doLayout：对 Table 进行重新布局。当 Table 或其祖先元素由隐藏切换为显示时
          // 可能需要调用此方法
          this.$refs.multipleTable.doLayout()
        })
      } finally {
        this.listLoading = false
      }
    }
  },
  created() {
    if (this.tableOption.requestApi) {
      this.handleList()
    }
  },
  props: {
    option: {
      type: Object,
      default() {
        return {}
      }
    }
  }
}
</script>

<style lang="scss">
.pagination-container {
  text-align: right;
}

.previewImg {
  width: 140px;
  height: 140px;
  margin-left: 10px;

  &:first-child {
    margin-left: 0;
  }
}
</style>
